package com.project.common.fundamental.controller;

import com.project.common.exception.BusinessException;
import com.project.common.exception.ErrorCode;
import com.project.common.exception.ErrorCodes;
import com.project.common.fundamental.response.body.ErrorBody;
import org.slf4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.Objects;

public interface ControllerAdvice {

    Logger getLogger();

    default boolean debugMode() {
        return false;
    }

    default ResponseEntity<?> handle(HttpStatus status, ErrorCode errorCode, Throwable exception) {
        getLogger().error(exception.getMessage(), exception);
        ErrorBody body = buildErrorBody(errorCode, exception);
        return ResponseEntity.status(status).body(body);
    }

    @ExceptionHandler(BusinessException.class)
    default ResponseEntity<?> handleBusinessException(BusinessException exception) {
        return handle(
            HttpStatus.INTERNAL_SERVER_ERROR,
            exception.getErrorCode(), exception);
    }

    @ExceptionHandler(BindException.class)
    default ResponseEntity<?> handleBindException(BindException exception) {
        String defaultMessage = Objects.requireNonNull(exception.getBindingResult().getFieldError()).getDefaultMessage();
        return handle(
            HttpStatus.BAD_REQUEST,
            ErrorCodes.Common.ERROR_VALIDATION_FAILED.errorCode(), new Exception(defaultMessage));
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    default ResponseEntity<?> handleTypeMismatchException(
        MethodArgumentTypeMismatchException exception) {
        return handle(
            HttpStatus.INTERNAL_SERVER_ERROR,
            ErrorCodes.Common.ERROR_PARAMETER_TYPE.errorCode(), exception);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    default ResponseEntity<?> handleNotValidException(MethodArgumentNotValidException exception) {
        String defaultMessage = exception.getBindingResult().getFieldError().getDefaultMessage();
        return handle(
            HttpStatus.BAD_REQUEST,
            ErrorCodes.Common.ERROR_VALIDATION_FAILED.errorCode(), new Exception(defaultMessage));
    }

    @ExceptionHandler(Exception.class)
    default ResponseEntity<?> handleException(Exception exception) {
        return handle(
            HttpStatus.INTERNAL_SERVER_ERROR,
            ErrorCodes.Common.ERROR_UNKNOWN_ERROR.errorCode(), exception);
    }

    default ErrorBody buildErrorBody(ErrorCode errorCode, Throwable exception) {
        if (exception.getCause() != null) {
            errorCode = ErrorCode.of(errorCode.getCode(), exception.getCause().getMessage());
        } else {
            errorCode = ErrorCode.of(errorCode.getCode(), exception.getMessage());
        }
        return debugMode() ? new ErrorBody(errorCode, exception) : new ErrorBody(errorCode);
    }
}
